program LoadIrrFile;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  SampleCommon,
  DIrr in '..\..\DSource\DIrr.pas',
  DIrr.GUI in '..\..\DSource\DIrr.GUI.pas',
  DIrr.Color in '..\..\DSource\DIrr.Color.pas',
  DIrr.Rect in '..\..\DSource\DIrr.Rect.pas',
  DIrr.Event in '..\..\DSource\DIrr.Event.pas',
  DIrr.Vector2D in '..\..\DSource\DIrr.Vector2D.pas',
  DIrr.Vector3D in '..\..\DSource\DIrr.Vector3D.pas',
  DIrr.Video in '..\..\DSource\DIrr.Video.pas',
  DIrr.Base in '..\..\DSource\DIrr.Base.pas',
  DIrr.Types in '..\..\DSource\DIrr.Types.pas',
  DIrr.Scene in '..\..\DSource\DIrr.Scene.pas',
  DIrr.Structs in '..\..\DSource\DIrr.Structs.pas',
  DIrr.Aabbox3D in '..\..\DSource\DIrr.Aabbox3D.pas',
  DIrr.Matrix4 in '..\..\DSource\DIrr.Matrix4.pas',
  DIrr.Texture in '..\..\DSource\DIrr.Texture.pas',
  DIrr.Collections in '..\..\DSource\DIrr.Collections.pas',
  DIrr.Consts in '..\..\DSource\DIrr.Consts.pas';

var
  DriverType: E_DRIVER_TYPE;
  Device: TIrrlichtDevice;
  Driver: TVideoDriver;
  Smgr: TSceneManager;
  Camera: TCameraSceneNode;
  Meta: TMetaTriangleSelector;
  Nodes: TDataList;
  i: U32;
  Node: TSceneNode;
  Selector: TTriangleSelector;
  Anim: TSceneNodeAnimator;
  Cube: TSceneNode;
  LastFPS, FPS: Integer;
  Str: String;
  Position: TVector3DF;
begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    DriverType := DriverChoiceConsole;
    if DriverType = EDT_COUNT then
    begin
      ExitCode := 1;
      Exit;
    end;

    // create device and exit if creation failed
    Device := createDevice(DriverType, [640, 480]);
    if Device = nil then
    begin
      ExitCode := 1;   // could not create selected driver.
      Exit;
    end;

    Device.SetWindowCaption('Load .irr file example');

    Driver := Device.GetVideoDriver;
    Smgr := Device.GetSceneManager;

    (*
    Now load our .irr file.
    .irr files can store the whole scene graph including animators,
    materials and particle systems. And there is also the possibility to
    store arbitrary user data for every scene node in that file. To keep
    this example simple, we are simply loading the scene here. See the
    documentation at ISceneManager::loadScene and ISceneManager::saveScene
    for more information. So to load and display a complicated huge scene,
    we only need a single call to loadScene().
    *)

    // load the scene
    if ParamCount > 1 then
      Smgr.LoadScene(PAnsiChar(AnsiString(ParamStr(1))))
    else
      Smgr.LoadScene('..\..\media\example.irr');

    (*
    Now we'll create a camera, and give it a collision response animator
    that's built from the mesh nodes in the scene
     we just loaded.
    *)
    Camera := Smgr.AddCameraSceneNodeFPS(nil, 50, 0.1);

    // Create a meta triangle selector to hold several triangle selectors.
    Meta := Smgr.CreateMetaTriangleSelector;

    (*
    Now we will find all the nodes in the scene and create triangle
    selectors for all suitable nodes.  Typically, you would want to make a
    more informed decision about which nodes to performs collision checks
    on; you could capture that information in the node name or Id.
    *)
    Nodes := TInputDataList.Create;

    Smgr.GetSceneNodesFromType(ESNT_ANY, Nodes); // Find all nodes

    for i := 0 to Nodes.GetCount - 1 do
    begin
      Node := TSceneNode(Nodes.GetItem(i));
      Selector := nil;

      case Node.GetType of
        ESNT_CUBE, ESNT_ANIMATED_MESH:
          // Because the selector won't animate with the mesh,
          // and is only being used for camera collision, we'll just use an approximate
          // bounding box instead of ((scene::IAnimatedMeshSceneNode*)node)->getMesh(0)
          Selector := Smgr.CreateTriangleSelectorFromBoundingBox(Node);

        ESNT_MESH, ESNT_SPHERE:
          // Derived from IMeshSceneNode
          Selector := Smgr.CreateTriangleSelector(TMeshSceneNode(Node).GetMesh, Node);

        ESNT_TERRAIN:
          Selector := Smgr.CreateTerrainTriangleSelector(TTerrainSceneNode(Node));

        ESNT_OCTREE:
          Selector := Smgr.CreateOctreeTriangleSelector(TMeshSceneNode(Node).GetMesh, Node);
      end;

      if Assigned(Selector) then
      begin
        // Add it to the meta selector, which will take a reference to it
        Meta.AddTriangleSelector(Selector);
        // And drop my reference to it, so that the meta selector owns it.
        Selector.Drop;
      end;
    end;

    (*
    Now that the mesh scene nodes have had triangle selectors created and added
    to the meta selector, create a collision response animator from that meta selector.
    *)

    Anim := Smgr.CreateCollisionResponseAnimator(
      Meta, Camera, Vector3DF(5, 5, 5), Vector3DF(0, 0, 0));
    Meta.Drop;    // I'm done with the meta selector now

    Camera.AddAnimator(Anim);
    Anim.Drop;    // I'm done with the animator now

    // And set the camera position so that it doesn't start off stuck in the geometry
    Camera.SetPosition(Vector3DF(0, 20, 0));

    // Point the camera at the cube node, by finding the first node of type ESNT_CUBE
    Cube := Smgr.GetSceneNodeFromType(ESNT_CUBE);
    if Assigned(Cube) then
    begin
      Cube.GetAbsolutePosition(Position);
      Camera.SetTarget(Position);
    end;

    (*
    That's it. Draw everything and finish as usual.
    *)

    LastFPS := -1;


    while (Device.Run and Assigned(Driver)) do
    begin
      if Device.IsWindowActive then
      begin
        Driver.BeginScene(True, True, IrrColor(200, 200, 200, 0));
        Smgr.DrawAll;
        Driver.EndScene;

        FPS := Driver.GetFPS;
        if LastFPS <> FPS then
        begin
          Str := 'Load Irrlicht File example - Irrlicht Engine [';
          Str := Str + String(Driver.GetName) + '] FPS:';
          Str := Str + IntToStr(FPS);

          Device.SetWindowCaption(PChar(Str));
          LastFPS := FPS;
        end;
      end;
    end;

    Nodes.Free;

    Device.Drop;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
